package org.jsola.hr.service.impl;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.jsola.admin.entity.SiteDO;
import org.jsola.common.DateKit8;
import org.jsola.core.Page;
import org.jsola.exception.ParamException;
import org.jsola.hr.common.ExampleKit;
import org.jsola.hr.constant.HrConstants;
import org.jsola.hr.dao.ICompanyBalanceRecordDAO;
import org.jsola.hr.dto.CompanyBalanceAddDTO;
import org.jsola.hr.dto.CompanyBalanceRecordAddDTO;
import org.jsola.hr.dto.CompanyBalanceRecordUpdateDTO;
import org.jsola.hr.dto.CompanyBalanceUpdateDTO;
import org.jsola.hr.entity.CompanyBalanceDO;
import org.jsola.hr.entity.CompanyBalanceRecordDO;
import org.jsola.hr.provider.IAdminProviderService;
import org.jsola.hr.query.CompanyBalanceRecordQuery;
import org.jsola.hr.service.ICompanyBalanceRecordService;
import org.jsola.hr.service.ICompanyBalanceService;
import org.jsola.hr.service.ICompanyService;
import org.jsola.hr.vo.CompanyBalanceRecordListVO;
import org.jsola.hr.vo.CompanyBalanceRecordVO;
import org.jsola.hr.vo.CompanyBalanceVO;
import org.jsola.hr.vo.CompanyVO;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 用工单位账面余额明细表
 *
 * @author zhr
 */
@Slf4j
@Service("hrCompanyBalanceRecordServiceImpl")
public class CompanyBalanceRecordServiceImpl implements ICompanyBalanceRecordService {

    @Autowired
    private ICompanyBalanceRecordDAO companyBalanceRecordDAO;

    @Autowired
    private ICompanyBalanceService companyBalanceService;

    @Autowired
    private ICompanyService companyService;

    @Autowired
    private IAdminProviderService adminProviderService;

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public CompanyBalanceRecordVO save(CompanyBalanceRecordAddDTO companyBalanceRecordAddDTO, TokenUser tokenUser) {
        // 转DO
        CompanyBalanceRecordDO companyBalanceRecordDO = companyBalanceRecordAddDTO.to(CompanyBalanceRecordDO.class);
        // 保存
        companyBalanceRecordDO = save(companyBalanceRecordDO, tokenUser.getSiteId(), tokenUser.getUserId());
        return companyBalanceRecordDO.to(CompanyBalanceRecordVO.class);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void saveRecord(CompanyBalanceRecordDO companyBalanceRecordDO, TokenUser tokenUser) {

        BigDecimal amount = companyBalanceRecordDO.getChangeAmount();
        String companyId = companyBalanceRecordDO.getCompanyId();
        log.info("==========本次操作对象:{}", JSON.toJSONString(companyBalanceRecordDO));
        // 操作金额可以为正负数
        ParamException.notNull(amount, "操作金额不能为空");
        ParamException.notNull(companyBalanceRecordDO.getYearly(), "年份不能为空");
        ParamException.notNull(companyBalanceRecordDO.getMonthly(), "月份不能为空");
        ParamException.hasText(companyId, "用工单位id不能为空");
        ParamException.notNull(companyBalanceRecordDO.getChangeType(), "操作类型不能为空");
        ParamException.hasText(companyBalanceRecordDO.getSubjectId(), "业务id不能为空");
        CompanyBalanceDO companyBalanceDO = companyBalanceService.selectByCompanyId(companyId, tokenUser.getSiteId());


        CompanyVO companyVO = companyService.selectBaseCompanyById(companyBalanceRecordDO.getCompanyId(), tokenUser.getSiteId());
        if (companyVO != null) {
            companyBalanceRecordDO.setCompanyName(companyVO.getName());
        }
        Map<String, SiteDO> siteDOMap = adminProviderService.selectSiteMap();
        SiteDO siteDO = siteDOMap.get(tokenUser.getSiteId());
        if (companyBalanceDO == null) {
            // 新增账户余额
            CompanyBalanceAddDTO companyBalanceAddDTO = new CompanyBalanceAddDTO();
            companyBalanceAddDTO.setAmount(amount);
            companyBalanceAddDTO.setCompanyId(companyId);
            CompanyBalanceVO companyBalanceVO1 = companyBalanceService.save(companyBalanceAddDTO, tokenUser);
            // 新增余额记录
            companyBalanceRecordDO.setBalanceId(companyBalanceVO1.getId());
            companyBalanceRecordDO.setSiteName(siteDO.getName());
            companyBalanceRecordDO.setBeforeAmount(BigDecimal.ZERO);
            companyBalanceRecordDO.setAfterAmount(BigDecimal.ZERO.add(amount));
            save(companyBalanceRecordDO, tokenUser.getSiteId(), tokenUser.getUserId());
        } else {
            // 修改账户余额
            CompanyBalanceUpdateDTO companyBalanceUpdateDTO = companyBalanceDO.to(CompanyBalanceUpdateDTO.class);
            companyBalanceUpdateDTO.setAmount(companyBalanceDO.getAmount().add(amount));
            companyBalanceService.update(companyBalanceUpdateDTO, tokenUser);
            companyBalanceRecordDO.setBalanceId(companyBalanceDO.getId());
            // 新增余额记录

            companyBalanceRecordDO.setSiteName(siteDO.getName());
            companyBalanceRecordDO.setBeforeAmount(companyBalanceDO.getAmount());
            companyBalanceRecordDO.setAfterAmount(companyBalanceDO.getAmount().add(amount));
            save(companyBalanceRecordDO, tokenUser.getSiteId(), tokenUser.getUserId());
        }
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int update(CompanyBalanceRecordUpdateDTO companyBalanceRecordUpdateDTO, TokenUser tokenUser) {
        //转DO
        CompanyBalanceRecordDO companyBalanceRecordDO = companyBalanceRecordUpdateDTO.to(CompanyBalanceRecordDO.class);
        //根据主键更新，只更新非null值
        return updateByIdSelective(companyBalanceRecordDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int deleteByIds(TokenUser tokenUser, Long... companyBalanceRecordIds) {
        return logicalDeleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), (Object[]) companyBalanceRecordIds);
    }


    @Override
    public CompanyBalanceRecordVO selectById(Long companyBalanceRecordId, String siteId) {
        CompanyBalanceRecordDO companyBalanceRecordDO = selectDOById(companyBalanceRecordId, siteId);
        if (companyBalanceRecordDO == null) {
            return null;
        }
        return companyBalanceRecordDO.to(CompanyBalanceRecordVO.class);
    }

    @Override
    public List<CompanyBalanceRecordListVO> select(CompanyBalanceRecordQuery companyBalanceRecordQuery, String siteId) {
        List<CompanyBalanceRecordDO> companyBalanceRecordDOList = selectDO(companyBalanceRecordQuery, siteId);
        if (CollectionUtils.isEmpty(companyBalanceRecordDOList)) {
            return companyBalanceRecordDOList == null ? null : new ArrayList<>();
        }
        return companyBalanceRecordDOList.stream()
                .map(companyBalanceRecordDO -> companyBalanceRecordDO.to(CompanyBalanceRecordListVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public int selectCount(CompanyBalanceRecordQuery companyBalanceRecordQuery, String siteId) {
        Example example = buildExample(companyBalanceRecordQuery, siteId);
        return companyBalanceRecordDAO.selectCountByExample(example);
    }

    @Override
    public Page<CompanyBalanceRecordListVO> selectPage(CompanyBalanceRecordQuery companyBalanceRecordQuery, String siteId) {
        Example example = buildExample(companyBalanceRecordQuery, siteId);
        Page<CompanyBalanceRecordDO> page = companyBalanceRecordDAO.selectPageByExample(example,
                companyBalanceRecordQuery.getPageNo(),
                companyBalanceRecordQuery.getPageSize());

        return page.to(CompanyBalanceRecordListVO.class);
    }

    @Override
    public CompanyBalanceRecordDO selectDOById(Long companyBalanceRecordId, String siteId) {
        return listById(companyBalanceRecordId, siteId);
    }

    @Override
    public List<CompanyBalanceRecordDO> selectDO(CompanyBalanceRecordQuery companyBalanceRecordQuery, String siteId) {
        Example example = buildExample(companyBalanceRecordQuery, siteId);
        return companyBalanceRecordDAO.selectByExample(example);
    }

    @Override
    public Page<CompanyBalanceRecordListVO> pageByCompanyId(CompanyBalanceRecordQuery companyBalanceRecordQuery, String siteId) {
        List<Long> balanceIds = companyBalanceRecordQuery.getBalanceIds();
        if(CollectionUtils.isEmpty(balanceIds)){
            return new Page<>(companyBalanceRecordQuery.getPageSize(), companyBalanceRecordQuery.getPageNo());
        }
        Example example = buildExample(companyBalanceRecordQuery, siteId);
        example.and()
                .andIn("balanceId", companyBalanceRecordQuery.getBalanceIds());
        // 创建时间，id倒序
        example.orderBy("gmtCreate").desc().orderBy("id").desc();
        Page<CompanyBalanceRecordDO> page = companyBalanceRecordDAO.selectPageByExample(example,
                companyBalanceRecordQuery.getPageNo(),
                companyBalanceRecordQuery.getPageSize());

        return page.to(CompanyBalanceRecordListVO.class);
    }

    @Override
    public List<CompanyBalanceRecordDO> selectAll() {
        Example example = new Example(CompanyBalanceRecordDO.class);
        example.and().andEqualTo("valid", true);
        return companyBalanceRecordDAO.selectByExample(example);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int init() {
        // 1.写入余额表
        List<CompanyBalanceRecordDO> recordDOS = selectAll();
        // 公司id分组
        Map<String, List<CompanyBalanceRecordDO>> companyIdListMap = recordDOS.stream()
                .collect(Collectors.groupingBy(CompanyBalanceRecordDO::getCompanyId));
        log.info("共{}个用工单位", companyIdListMap.size());
        long l = System.currentTimeMillis();
        int countSave = 0;
        for (Map.Entry<String, List<CompanyBalanceRecordDO>> companyIdIdListEntry : companyIdListMap.entrySet()) {
            String companyId = companyIdIdListEntry.getKey();
            BigDecimal amount = BigDecimal.ZERO;
            List<CompanyBalanceRecordDO> companyBalanceRecordDOS = companyIdListMap.get(companyId);
            String siteId = companyBalanceRecordDOS.get(0).getSiteId();
            String userId = companyBalanceRecordDOS.get(0).getCreateUserId();
            TokenUser tokenUser = new TokenUser();
            tokenUser.setUserId(userId);
            tokenUser.setSiteId(siteId);
            for (CompanyBalanceRecordDO recordDO : companyBalanceRecordDOS) {
                amount = amount.add(recordDO.getChangeAmount());
            }
            CompanyBalanceDO companyBalanceDO = companyBalanceService.selectByCompanyId(companyId, siteId);
            if (companyBalanceDO == null) {
                CompanyBalanceAddDTO balanceAddDTO = new CompanyBalanceAddDTO();
                balanceAddDTO.setCompanyId(companyId);
                balanceAddDTO.setAmount(amount);
                companyBalanceService.save(balanceAddDTO, tokenUser);
                countSave++;
            }
        }
        log.info("1. 写入余额表共：{}条", countSave);
        log.info("耗时：{}ms", System.currentTimeMillis() - l);

        // 2.更新明细表余额id
        int countUpdate = 0;
        List<CompanyBalanceDO> companyBalanceDOS = companyBalanceService.selectAll();
        Map<String, List<CompanyBalanceDO>> stringListMap = companyBalanceDOS.stream()
                .collect(Collectors.groupingBy(CompanyBalanceDO::getCompanyId));
        for (CompanyBalanceRecordDO recordDO : recordDOS) {
            List<CompanyBalanceDO> companyBalanceDOS1 = stringListMap.get(recordDO.getCompanyId());
            if (!CollectionUtils.isEmpty(companyBalanceDOS1)) {
                CompanyBalanceRecordUpdateDTO updateDTO = new CompanyBalanceRecordUpdateDTO();
                String siteId = companyBalanceDOS1.get(0).getSiteId();
                String userId = companyBalanceDOS1.get(0).getCreateUserId();
                TokenUser tokenUser = new TokenUser();
                tokenUser.setUserId(userId);
                tokenUser.setSiteId(siteId);
                updateDTO.setId(recordDO.getId());
                updateDTO.setBalanceId(companyBalanceDOS1.get(0).getId());
                update(updateDTO, tokenUser);
                countUpdate++;
            }
        }
        log.info("2. 更新明细表共：{}条", countUpdate);


        // 3.重新计算金额，以用工单位为最小粒度
        int count = 0;
        Map<String, List<CompanyBalanceRecordDO>> collect = selectAll().stream()
                .sorted(Comparator.comparing(CompanyBalanceRecordDO::getGmtCreate))
                .collect(Collectors.groupingBy(CompanyBalanceRecordDO::getCompanyId));
        for (CompanyBalanceDO balanceDO : companyBalanceDOS) {
            String companyId = balanceDO.getCompanyId();
            BigDecimal amount = balanceDO.getAmount();
            List<CompanyBalanceRecordDO> recordDOList = collect.get(companyId);
            for (CompanyBalanceRecordDO recordDO : recordDOList) {
                TokenUser tokenUser = new TokenUser();
                tokenUser.setUserId(recordDO.getCreateUserId());
                tokenUser.setSiteId(recordDO.getSiteId());

                BigDecimal changeAmount = recordDO.getChangeAmount();
                CompanyBalanceRecordUpdateDTO updateDTO = new CompanyBalanceRecordUpdateDTO();
                updateDTO.setId(recordDO.getId());
                updateDTO.setBeforeAmount(amount);
                updateDTO.setAfterAmount(amount.add(changeAmount));
                update(updateDTO, tokenUser);
                count++;
            }
        }
        log.info("3.重新计算金额共：{}条", count);

        return countSave;
    }


    /**
     * 根据查询参数，构建example
     *
     * @param companyBalanceRecordQuery 查询参数
     * @param siteId                    所属站点id
     * @return example
     */
    private Example buildExample(CompanyBalanceRecordQuery companyBalanceRecordQuery, String siteId) {
        Example example = new Example(CompanyBalanceRecordDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        if (Objects.nonNull(companyBalanceRecordQuery.getChangeType())) {
            example.and()
                    .andEqualTo("changeType", companyBalanceRecordQuery.getChangeType());
        }
        if (Objects.nonNull(companyBalanceRecordQuery.getBalanceId())) {
            example.and()
                    .andEqualTo("balanceId", companyBalanceRecordQuery.getBalanceId());
        }
        if (companyBalanceRecordQuery.getGmtCreateStart() != null &&
                companyBalanceRecordQuery.getGmtCreateEnd() != null) {
            example.and().andBetween("gmtCreate",
                    DateKit8.getStartTime(DateKit8.getFirstDayOfMonth(companyBalanceRecordQuery.getGmtCreateStart())),
                    DateKit8.getEndTime(DateKit8.getLastDayOfMonth(companyBalanceRecordQuery.getGmtCreateEnd())));
        }
        if (companyBalanceRecordQuery.getDeleteStatus() != null) {
            example.and().andEqualTo("deleteStatus", companyBalanceRecordQuery.getDeleteStatus());
        }
        // 排序
        ExampleKit.setExampleOrder(example, companyBalanceRecordQuery.getOrders());
        return example;
    }
}




